/**
 * @file Matrix.h
 * @brief 设计矩阵类Matrix
 * @author Goodtea (goodgooodtea2003@gmail.com)
 * @date 2023-10-14
 */

#ifndef MATRIX_H_
#define MATRIX_H_ //头文件

#include<iostream>
using namespace std;

/**
 * @brief 定义矩阵类
 */
class Matrix{
private:
    int row; // 矩阵的行数
    int col; // 矩阵的列数
    double** entry; // 二重指针构造矩阵

public:
    Matrix(int _row, int _col); // 构造函数
    Matrix(const Matrix& other); // 拷贝函数
    ~Matrix(); // 析构函数
    void change_value(int m, int n, double value); // 赋值函数
    void read_matrix(); // 输入矩阵
    void print_matrix(); // 输出矩阵
    Matrix operator+(const Matrix &other) const; //矩阵加法
    Matrix operator-(const Matrix &other) const; //矩阵减法  
    Matrix operator*(double scalar) const; //矩阵数乘 A*c
    friend Matrix operator*(double scalar, const Matrix &M){ return M * scalar; }; // 矩阵数乘 c*A
    Matrix operator*(const Matrix &other) const; //矩阵乘法    
};

/**
 * @brief 构造函数，读入矩阵的行与列，并动态分配一个元素初值皆为0的二维数组
 * @param  _row             My Param doc
 * @param  _col             My Param doc
 */
Matrix::Matrix(int _row, int _col){
    row = _row;
    col = _col;
    entry = new double*[row];
    for(int i = 0; i < row; i++){
        entry[i] = new double[col];
        for(int j = 0; j < col; j++){
            entry[i][j] = 0; 
        }
    }
}

/**
 * @brief 拷贝函数，读入一整个矩阵，并动态分配一个各元素皆相同的二维数组
 * @param  other            My Param doc
 */
Matrix::Matrix(const Matrix &other){
    row = other.row;
    col = other.col;
    entry = new double*[row];
    for(int i = 0; i < row; i++){
        entry[i] = new double[col];
        for(int j = 0; j < col; j++){
            entry[i][j] = other.entry[i][j];
        }
    }
}

/**
 * @brief 析构函数
 */
Matrix::~Matrix(){
    for(int i = 0; i < row; i++){
        if(entry[i] != nullptr){
            delete [] entry[i];
        }
    }
    if(entry != nullptr){
        delete [] entry;
    }
}

/**
 * @brief 赋值函数，改变矩阵中某元素的值
 * @param  m                My Param doc
 * @param  n                My Param doc
 * @param  value            My Param doc
 */
void Matrix::change_value(int m, int n, double value){
    if(m >= row || n >= col){
        cout << "Invalid! The index exceeds the range!" << endl;
    }
    else{
        entry[m][n] = value;
    }
}

/**
 * @brief 输入矩阵，依次输入矩阵row行col列的元素并赋值给矩阵
 */
void Matrix::read_matrix(){
    for(int i = 0; i < row; i++){
        for(int j = 0; j < col; j++){
            double value;
            cin >> value;
            change_value(i, j, value);
        }
    }
}

/**
 * @brief 输出矩阵，依次输出矩阵row行col列的元素
 */
void Matrix::print_matrix(){
    for(int i = 0; i < row; i++){
        for(int j = 0; j < col; j++){
            cout << entry[i][j] << " ";
        }
        cout << endl;
    }
}

/**
 * @brief 矩阵加法，加法运算符重载，使得矩阵A、B相加时可以使用 A + B 的语句
 * @param  other            My Param doc
 * @return Matrix 
 */
Matrix Matrix::operator+(const Matrix &other) const{
    Matrix matrix_sum(row, col);
    if(row != other.row || col != other.col){
        cout << "Warning! The two matrices are not in the same dimension!" << endl;
        exit(-1);
    }
    for(int i = 0; i < row; i++){
        for(int j = 0; j < col; j++){
            double value_sum = entry[i][j] + other.entry[i][j];
            matrix_sum.change_value(i, j, value_sum);
        }
    }
    return matrix_sum;
}

/**
 * @brief 矩阵减法，减法运算符重载，使得矩阵A、B相减时可以使用 A - B 的语句 
 * @param  other            My Param doc
 * @return Matrix 
 */
Matrix Matrix::operator-(const Matrix & other) const{
    return *this + (-1) * other;
}

/**
 * @brief 矩阵数乘，乘法运算符重载，使得矩阵A和标量c相乘时可以使用 A * c 的语句
 * @param  scalar           My Param doc
 * @return Matrix 
 */
Matrix Matrix::operator*(double scalar) const{
    Matrix matrix_scalar(row, col);
    for(int i = 0; i < row; i++){
        for(int j = 0; j < col; j++){
            double value_scalar = scalar * entry[i][j];
            matrix_scalar.change_value(i, j, value_scalar);
        }
    }
    return matrix_scalar;
}

/**
 * @brief 矩阵乘法，乘法运算符重载，使得矩阵A、B相乘时可以使用 A * B 的语句
 * @param  other            My Param doc
 * @return Matrix 
 */
Matrix Matrix::operator*(const Matrix &other) const{
    Matrix matrix_multiple(row, other.col);    
    if(col != other.row){
        cout << "Warning! The two matrices are not in the same dimension!" << endl;
        exit(-1);        
    }
    for(int i = 0; i < row; i++){
        for(int j = 0; j < other.col; j++){
            double value_multiple = 0;
            for(int k = 0; k < col; k++){
                value_multiple += entry[i][k] * other.entry[k][j];
            }
            matrix_multiple.change_value(i, j, value_multiple);
        }
    }
    return matrix_multiple;
}

#endif